#include "temu.h"

/* We use the POSIX regex functions to process regular expressions.
 * Type 'man regex' for more information about POSIX regex functions.
 */
#include <sys/types.h>
#include <regex.h>
#include <elf.h>
uint32_t Mark_Address(char *str);

enum {
    NOTYPE = 256, EQ, LESSEQU, GREATEREQU, NUM, HEXNUM, ADD, NEG , NEQ , REG, AND, OR, MARK,

    /* TODO: Add more token types */

};
static struct rule {
    char *regex;
    int token_type;
} rules[] = {

    /* TODO: Add more rules.
     * Pay attention to the precedence level of different rules.
     */
    {"0x[0-9a-fA-F]+", HEXNUM }, // hexadecimal number
    {"[0-9]+", NUM},   // number
    {"\\$[a-z]+", REG},  // register
    {" +", NOTYPE},    // spaces
    {"\\+", '+'},         // plus
    {"\\-", '-'},         // minus
    {"\\*", '*'},         // multiple
    {"\\/", '/'},         // divide
    {"\\%", '%'},         // mod
    {"\\(", '('},         // lp
    {"\\)", ')'},         // rp
    {"<=", LESSEQU},   // <=
    {">=", GREATEREQU},// >=
    {">", '>'},           // greater
    {"<", '<'},           // less
    {"\\&\\&", AND},   // and
    {"\\|\\|", OR},    // or
    {"!=", NEQ},         // notequal
    {"\\!", '!'},         // not
    {"==", EQ},      // equal
    {"\\b[a-zA-Z0-9_]+\\b" , MARK},
};

const int level_of_operator[6][7] = {{AND, OR}, {EQ, LESSEQU, GREATEREQU, '>', '<' , NEQ}, {'+', '-'}, {'*', '/', '%'} , {'!' , NEG , ADD} };
//const int single[6] = {'!' , NEG , ADD};

#define NR_REGEX (sizeof(rules) / sizeof(rules[0]) )

static regex_t re[NR_REGEX];

/* Rules are used for many times.
 * Therefore we compile them only once before any usage.
 */
void init_regex() {
    int i;
    char error_msg[128];
    int ret;

    for (i = 0; i < NR_REGEX; i ++) {
        ret = regcomp(&re[i], rules[i].regex, REG_EXTENDED);

        if (ret != 0) {
            regerror(ret, &re[i], error_msg, 128);
            panic("regex compilation failed: %s\n%s", error_msg, rules[i].regex);
        }
    }
}

typedef struct token {
    int type;
    char str[32];
} Token;

Token tokens[32];
int nr_token;

static bool make_token(char *e) {
    int position = 0;
    int i;
    regmatch_t pmatch;

    nr_token = 0;

    while (e[position] != '\0') {
        /* Try all rules one by one. */
        for (i = 0; i < NR_REGEX; i ++) {
            if (regexec(&re[i], e + position, 1, &pmatch, 0) == 0 && pmatch.rm_so == 0) {
                char *substr_start = e + position;
                int substr_len = pmatch.rm_eo;

                Log("match rules[%d] = \"%s\" at position %d with len %d: %.*s",
                    i, rules[i].regex, position, substr_len, substr_len, substr_start);
                position += substr_len;

                /* TODO: Now a new token is recognized with rules[i]. Add codes
                 * to record the token in the array `tokens'. For certain types
                 * of tokens, some extra actions should be performed.
                 */

                switch (rules[i].token_type) {
                case NOTYPE:
                    break;

                default:
                    tokens[nr_token].type = rules[i].token_type;
                    sprintf(tokens[nr_token].str, "%.*s", substr_len, substr_start);
                    nr_token ++;
                }

                break;
            }
        }

        if (i == NR_REGEX) {
            printf("no match at position %d\n%s\n%*.s^\n", position, e, position, "");
            return false;
        }
    }

    return true;
}
int getlevel(int x) {
    int i , j;
    for (j = 0; j < 5; j++) {
        for (i = 0; i < 6; i++) {
            if (level_of_operator[j][i] == x) {
                return j;
            }
        }
    }
    return -1;
}

bool matchError;
bool check_parentheses(int p, int q) {
    int cnt = 0, i;

    if (tokens[p].type != '(' || tokens[q].type != ')')
        return false;

    for (i = p; i <= q; i ++) {
        cnt += (tokens[i].type == '(' ? 1 : (tokens[i].type == ')' ? -1 : 0));

        if (cnt < 0)
            return false;
    }

    return cnt ? matchError = true ,  false : true;
}

int find_lp(int p, int q) {
    int cnt = 0, i;
    if (tokens[q].type != ')') {
        return -1;
    } else {
        for (i = q; i >= p; i --) {
            cnt += (tokens[i].type == '(' ? -1 : (tokens[i].type == ')' ? 1 : 0));
            if (!cnt) {
                return i;
            }
        }
    }

    return -1;
}
int find_rp(int p, int q) {
    int cnt = 0, i;
    if (tokens[p].type != '(') {
        return -1;
    } else {
        for (i = p; i <= q; i ++) {
            cnt += (tokens[i].type == '(' ? 1 : (tokens[i].type == ')' ? -1 : 0));
            if (!cnt) {
                return i;
            }
        }
    }

    return -1;
}



uint32_t common_operation(uint32_t x, uint32_t y, int c){
//printf("com %u %u %d\n",x,y,c);
    switch (c) {
    case '+' :
        return x + y;

    case '-' :
        return x - y;

    case '*' :
        return x * y;

    case '/' :
        return x / y;

    case '%' :
        return x % y;

    case '>' :
        return x > y;

    case '<' :
        return x < y;

    case EQ :
        return x == y;

    case NEQ:
        return x != y;
    
    case AND:
        return x && y;

    case OR :
        return x || y;

    case '!':
        return !x;

    case LESSEQU :
        return x <= y;

    case GREATEREQU :
        return x >= y;

    case NEG :
        return -x;

    case ADD :
        //return swaddr_read(x , 4);

    default :
        return 0;
    }
}

uint32_t eval(int p, int q) {
    int i , j;
    uint32_t res;
    if (p > q) {
        
    } else if (p == q) {
        if (tokens[p].type == MARK){
            //return Mark_Address(tokens[p].str);
        }else
    	if (tokens[p].type == NUM || tokens[p].type == HEXNUM) {
            sscanf(tokens[p].str + (tokens[p].type == NUM ? 0 : 2) , tokens[p].type == NUM ? "%d" : "%x" , &res);
	        return res;
        } else if (tokens[p].type == REG) {
            if (strcmp(tokens[p].str + 1, "pc") == 0) {    
                return cpu.pc;
            }
        }
        for (i = R_ZERO; i <= R_RA; i++) {
	        if (strcmp(tokens[p].str + 1, regfile[i]) == 0) {
                return reg_w(i);
            }
        }
    } else if (check_parentheses(p, q)) {
        return eval(p + 1, q - 1);
    }else{
      for (j = 0; j < 4; j++) {
        for (i = q; i >= p; i--) { 
		   if (tokens[i].type == ')') {
              i = find_lp(p, i);
            }
            if (getlevel(tokens[i].type) == j){
//printf("%d %d %d\n",i,tokens[i].type,j); 
                return common_operation(eval(p, i - 1), eval(i + 1, q) , tokens[i].type);
            }
        }
      }
      
	  for (i = p;i < q;i++){
	    if (getlevel(tokens[i].type) == 4){
	    	if (tokens[i].type == '(') {
              i = find_rp(i , q);
            }
             res = eval(i + 1, q);
            //printf("fa %d %d %u\n",i+1,q,res);
           return common_operation(res , 0 , tokens[i].type);
        }  
	  }
	  
   }
		
    matchError = true;
    return -1;
}

uint32_t expr(char *e, bool *success) {
    int i;
    uint32_t result;
    if (!make_token(e)) {
        *success = false;
        return 0;
    }

    *success = true;
    /* TODO: Insert codes to evaluate the expression. */
    for (i = 0;i < nr_token;i++){
    	if (tokens[i].type == '*' && (i == 0 || (tokens[i - 1].type != HEXNUM && tokens[i - 1].type != NUM && tokens[i - 1].type != ')' && tokens[i - 1].type != REG)) ){
    		tokens[i].type = ADD;
		}
		if (tokens[i].type == '-' && (i == 0 || (tokens[i - 1].type != HEXNUM && tokens[i - 1].type != NUM && tokens[i - 1].type != ')' && tokens[i - 1].type != REG)) ){
    		tokens[i].type = NEG;
		}
	}
/*for (i = 0;i < nr_token;i++){
	printf("%d ",tokens[i].type);
}puts("");*/
	result = eval(0, nr_token - 1);

    *success = !matchError;
    return result;
}

